home *** CD-ROM | disk | FTP | other *** search
- title 'MLOAD MULTI-FILE HEX LOAD UTILITY'
- ;
- ; *********************************
- ; * MLOAD.ASM *
- ; * Multi-file Hex Load Utility *
- ; * for CP/M *
- ; *********************************
- ;
- ;
- ; Replacement for the cp/m "LOAD" program: this program
- ; fixes many of the problems associated with the "CP/M"
- ; load program, and adds many new features.
- ;
- ; ----------------
- ;
- ; Rev 2.5
- ; 03/10/88
- ; Property of NightOwl Software, Inc. Fort Atkinson, WI 53538
- ; Written by Ron Fowler, Nightowl Software, Inc.
- ;
- ; ----------------
- ; Notice: this program is NOT public domain; copyright is retained by
- ; NightOwl Software, Inc. of Fort Atkinson, WI ... All Rights Reserved.
- ;
- ; License is granted for free use and re-distribution this program, as
- ; long as such use and re-distribution is done without profit.
- ;
- ; ----------------
- ;
- ; modification history:
- ;
- ; 2.5 (WOD) This version corrects a bug that overlayed the first six
- ; bytes of the CCP. The error did not show up unless a
- ; jump to the CCP was done without a warm boot since MLOAD
- ; used. This source file has been modified here with
- ; concurrence of the author of MLOAD, Ron Fowler.
- ;
- ; 2.4 (RGF) We apologize for this relatively insubstantial update,
- ; but someone has caused what we consider to be a problem,
- ; by making changes to the program, and re-releasing under
- ; the same version number. The changes in this case were
- ; conversion of the opcode fields (but not the comments,
- ; can you believe that??) of every line to upper case! That
- ; totally invalidated the CRC of the source file, since there
- ; are now two different MLOAD 2.3's running around.
- ;
- ; We DO NOT want these stupid mixed upper/lower case changes.
- ; Someone somewhere has decided that this is the way assembly
- ; language source should be, and we most VEHEMENTLY disagree.
- ; It's a pain in the neck to make changes to and we don't
- ; care to run our programs through conversion programs every
- ; time we make changes.
- ;
- ; So ... leave the case of this file AS IS. Any changes made
- ; to this program and not co-ordinated through us may very
- ; well endanger availability of source code when we make
- ; future updates. 'nuff said --NightOwl Software
- ;
- ; 2.3 (RGF) Trivial cosmetic changes
- ; 2.2 (RGF) Modified copyright notice to show new owner of the
- ; program.
- ; 2.1 (RGF) Fixed problem on disk-full when writing output file
- ; (mload previously didn't error out on a full disk)
- ; 2.0 (RGF) Added the ability to pre-load a non-hex file, allowing
- ; mload to be used to load hex file patches (obviating any
- ; need to use DDT). The normal mload syntax is preserved.
- ; the first (and only the first) filespec (after the "=",
- ; if used) may be non-hex; the filetype must be specified.
- ; Examples:
- ;
- ; 1) mload ws.com,wspatch
- ; 2) mload MEXTEST=MEX112.COM,MXO-US13
- ; 3) mload ws.ovr,ovrpatch
- ;
- ; The first example loads WS.COM, overlays it with
- ; wspatch.hex, and writes the output to WS.COM. The
- ; second example loads MEX112.COM, overlays it with
- ; MXO-US13.HEX, and writes the output file to MEXTEST.COM.
- ; (note that the second example is the recommended technique,
- ; since it preserves the original file). The third example
- ; loads WS.OVR and patches it with the file "OVRPATCH.HEX".
- ;
- ; Also added this rev: ZCPR2-style du specs are now fully
- ; supported, for both input and output files. Thus, the
- ; following command lines are permissable:
- ;
- ; b3>mload a4:myfile.com=0:bigfil,b6:patch1,c9:patch2
- ; a6>mload b5:=c3:mdm717.com,mdmpatch
- ;
- ; After loading, an additional information line is now printed
- ; in the statistics report, which displays the true size of the
- ; saved image (the previous report was technically correct, but
- ; could result in confusion for certain kinds of files with
- ; imbedded "DS" and "ORG" statements in the original source code).
- ;
- ; 1.0 - 1.4 (RGF) change log removed to conserve space
- ;
- ; originally written by ron fowler, fort atkinson, wisconsin
- ;
- ;
- ;
- ; For assembly with asm.com or mac (delete above title line if
- ; assembling with asm.com).
- ;
- ; This program is a replacement for the cp/m "LOAD" program.
- ; Why replace "LOAD"? well... LOAD.COM has a few deficiencies.
- ; For example, if your hex file's origin is above 100h, LOAD.COM
- ; prepends blank space to the output file to insure it will work
- ; as a CP/M transient. It cares not if the file is not intended
- ; as a CP/M transient. it also doesn't like hex records with mixed
- ; load addresses (for example, one that loads below a previous record --
- ; which is a perfectly legitimate happenstance). Also, LOAD.COM
- ; can load only one program at a time, and has no provision for
- ; a load bias in the command specification. Finally, there is no
- ; provision for user specification of the output file name.
- ;
- ;
- ; Hence, this program....
- ;
- ;------------------------------------------------------------
- ;
- ; Syntax is as follows:
- ;
- ; mload [<outnam=] <filename>[,<filename>...] [bias]
- ;
- ; where <outnam> is the (optional!;) output file name (only the drive
- ; spec and primary filename may be specified; the output filetype is
- ; derived exclusively from the 3-byte string at 103h within MLOAD),
- ; <filename> specifies files to load and <bias> is the offset within
- ; the saved image to apply when loading the file.
- ;
- ; MLOAD with no arguments prints a small help message -- this message
- ; is also printed whenever a command line syntax error occurs.
- ;
- ; Filenames may contain drive/user specs, and must not contain wildcards.
- ; Input filenames must be separated by commas, and a space is required
- ; between the last filename and the optional bias.
- ;
- ; A load information summary is printed at the successful conclusion
- ; of the load. Any errors in loading will generally include the name
- ; of the file in question.
- ;
- ; If no output filename is specified, it will be derived from the first
- ; input filename, with filetype of 'COM', if not otherwise specified
- ; (this default filetype may be patched directly into mload via DDT
- ; (or with MLOAD itself, using a patch file) -- its location is at 103H
- ; in MLOAD.COM). Note that a command line of the form "C:=<FILENAME>"
- ; will place the output file on the "C" drive with the same primary
- ; filename as the input file.
- ;
- ; In its simplest form, MLOAD's syntax is identical to LOAD.COM; thus
- ; there should be no problem in learning to use the new program. The
- ; only significant difference here is that, under LOAD.COM, all files
- ; are output starting at 100h, even if they originate elsewhere. MLOAD
- ; outputs starting at the hex file origin (actually, the first hex rec-
- ; ord specifies the output load address). The bias option may be used
- ; to override this.
- ;
- ; An example should clarify this. Suppose you have a file that loads
- ; at 1000h. LOAD.COM would save an output file that begins at 100h and
- ; loads past 1000h (to wherever the program ends). MLOAD will save an
- ; output file starting from 1000h only. If, for some reason you need the
- ; file to start at 100h in spite of its 1000h origin (i can think of sev-
- ; eral circumstances where this would be necessary), you'd have to specify
- ; a bias to mload. thus, using this example, "MLOAD MYFILE 0F00" would do.
- ;
- ; Note that this program re-initializes itself each time it is run.
- ; Thus, if your system supports a direct branch to the tpa (via a zero-length
- ; .COM file, or the ZCPR "GO" command), you may safely re-execute MLOAD.
- ;
- ; Please report any bugs, bug fixes, or enhancements to
- ;
- ; "FORT FONE FILE FOLDER" rcpm/cbbs
- ; Fort Atkinson, Wisconsin
- ; (414) 563-9932 (no ring back)
- ;
- ; --Ron Fowler
- ; 03/08/84 updated 1/31/85
- ;
- ;------------------------------------------------------------
- ;
- ; CP/M equates
- ;
- warmbt equ 0 ;warm boot
- system equ 5 ;system entry (also top of mem pntr)
- dfcb equ 5ch ;default file control block
- ft equ 9 ;fcb offset to filetype
- tbuf equ 80h ;default buffer
- tpa equ 100h ;transient program area
- eof equ 1ah ;cp/m end-of-file mark
- fcbsiz equ 33 ;size of file control block
- ;
- ; CP/M system calls
- ;
- pcharf equ 2 ;print char
- seldf equ 14 ;select disk drive
- openf equ 15 ;open file
- closef equ 16 ;close file
- fsrchf equ 17 ;search for first
- fsrchn equ 18 ;search for next
- erasef equ 19 ;delete file
- readf equ 20 ;read record
- writef equ 21 ;write record
- creatf equ 22 ;create file
- getdrf equ 25 ;return dflt drive #
- sdmaf equ 26 ;set dma address
- gsuser equ 32 ;get/set user #
- rrand equ 33 ;read random
- wrand equ 34 ;write random
- filszf equ 35 ;compute file size
- srand equ 36 ;set random
- ;
- ; ASCII character constants
- ;
- cr equ 13
- lf equ 10
- bel equ 7
- tab equ 9
- ;
- ; without further ado...
- ;
- org tpa
- ;
- jmp begin ;jump over default output filetype
- ;
- ; the default output filetype is located at 103h for easy patching
- ;
- outtyp: db 'COM'
- ;
- begin: lxi h,0 ;save system stackpointer
- dad sp
- shld spsave
- lxi sp,stack ;load local stack
- call ilprnt ;sign on
- db 'MLOAD ver. 2.5 Copyright (C) 1983, 1984, 1985'
- db cr,lf
- db 'by NightOwl Software, Inc.'
- db cr,lf,0
- call setup ;initialize
- main: call nxtfil ;parse and read next input file
- jc done ;no more...
- call lodfil ;yep, load it
- call closfl ;close it (in case MP/M or TurboDOS)
- jmp main ;maybe more
- done: call wrtfil ;write the output file
- ;
- ; exit to cp/m
- ;
- exit: lxi d,tbuf ;restore dma address
- mvi c,sdmaf
- call bdos
- lda system+2 ;get top of memory pointer
- sui 9 ;allow for ccp+slop
- lxi h,hiload+1 ;highest load address
- sub m ;above ccp?
- jc warmbt ;then warm-boot
- lhld spsave ;nope, ccp still in memory
- sphl ;restore its stack
- ret ;return to ccp
- ;
- ; load program initialization
- ;
- setup: lxi h,varset ;initialize variables
- lxi d,vars
- mvi b,varlen ;by moving in default values
- call move
- lhld cmdptr ;get first free mem pointer
- xchg ;in de
- lxi h,tbuf ;point to command tail bufr
- mov a,m ;get its length
- inx h
- ora a ;does it have any length?
- jz help ;nope, go give usage help
- mov b,a ;yep, get length to b
- call move ;move cmd tail to buffer
- xchg ;end of dest to hl
- mvi m,0 ;stuff a terminator
- inx h ;point to first free memory
- shld filbuf ;set up file buffer
- xchg ;file bufr adrs to de
- lhld system+1 ;get top of memory pointer
- xra a ;round system to page boundary
- sub e
- mov c,a ;with result in bc
- mov a,h
- sui 9 ;allow for ccp
- sbb d
- mov b,a
- xchg ;buffer pointer to hl
- nitmem: mvi m,0 ;clear buffer
- inx h
- dcx b
- mov a,b
- ora c
- jnz nitmem
- ;
- ; look for a bias specification in command line
- ;
- lhld cmdptr ;point to command buffer-1
- dcx h
- call scanbk ;scan past blanks
- ora a ;no non-blank chars?
- jz help ;then go print help text
- fndspc: inx h ;point to next
- mov a,m ;fetch it
- ora a ;test it
- rz ;line ended, return
- cpi ' ' ;nope, test for blank
- jnz fndspc ;not blank, continue
- call scanbk ;skip blanks
- ora a ;end-of-line?
- rz ;return if so
- ;
- ; hl points to bias in command line
- ;
- lxi d,0 ;init bias
- call hexdig ;insure a hex digit
- jc synerr ;bad...
- hexlp: mov a,m ;no. get next char
- inx h ;skip over it
- call hexdig ;test for hex digit
- jnc digok ;jump if good hex digit
- ora a ;must end on null terminator
- jnz synerr
- xchg ;good end, get bias to hl
- shld bias ;stuff it
- ret ;done
- digok: xchg ;bias to hl
- dad h ;skift left 4 to make room
- dad h ; for new hex digit
- dad h
- dad h
- xchg ;back to de
- add e ;add in new digit
- mov e,a
- jnc hexlp ;jump if no 8-bit ovfl
- inr d ;carry
- jmp hexlp
- ;
- ; parse next input name, and open resultant file
- ;
- nxtfil: lhld cmdptr ;get command line pointer
- next2: lxi d,dfcb ;destination fcb
- call fparse ;parse a filename
- cpi '=' ;stopped on output specifier?
- jnz noteq
- lda outnam+2 ;insure no name yet specified
- cpi ' '
- jnz synerr ;syntax error if already named
- lda outflg ;already been here?
- ora a
- jnz synerr ;can't be here twice
- inr a ;flag that we've been here
- sta outflg
- inr b ;insure no ambiguous output name
- dcr b
- jnz afnerr
- inx h ;skip over '='
- push h ;save cmd line pointer
- lxi h,dfcb-1 ;move the name to output name hold
- lxi d,outnam
- mvi b,13 ;drive spec too
- call move
- pop h ;restore command line pointer
- jmp next2 ;go parse another
- noteq: cpi ',' ;stopped on comma?
- jz gcomma ;jump if so
- mvi m,0 ;nope, insure end of input
- jmp nxt2 ;don't advance over fake end
- gcomma: inx h ;skip over comma
- nxt2: shld cmdptr ;save new command line pntr
- mov a,b ;get ambig char count
- ora a ;test it
- jnz afnerr ;allow no ambig characters
- sta bufptr ;force a disk read
- lxi d,dfcb+1 ;look at parsed filename
- ldax d
- cpi ' ' ;blank? (input ended?)
- stc ;get carry ready in case so
- rz ;return cy if input gone
- dcx d ;nope, point de to start of fcb
- open2: push d ;try to open the file
- mvi c,openf
- call bdos
- pop d
- inr a ;return=0ffh?
- jnz openok ;jump if not
- ;
- ; file not found: if filetype blank, set to 'hex' and try again
- ;
- lxi h,dfcb+ft ;point to file type
- mov a,m ;anything there?
- cpi ' '
- jnz fnferr ;yes, so file not found
- mvi m,'H' ;nope, fill in 'hex'
- inx h
- mvi m,'E'
- inx h
- mvi m,'X'
- jmp open2 ;go try again
- ;
- ; here after a good file open
- ;
- openok: call hexchk ;is this a hex file?
- rz ;if so, all done
- lxi h,comflg ;no, get pointer to flag
- mov a,m ;loading first file?
- ora a
- rnz ;if not, ignore type, consider hex
- inr m ;else, set the flag
- ret
- ;
- ; load current file
- ;
- lodfil: lxi h,comflg ;loading a com file?
- mov a,m ;get flag
- ani 1
- jnz lodcom ;jump if so
- lhld bias ;else get bias on top of stack
- push h
- ;
- ; load a hex record
- ;
- loadlp: call gnb ;get next file byte
- sbi ':' ;look for start-record mark
- jnz loadlp ;scan until found
- sta cksum ;got it, init checksum to zero
- mov d,a ;upper byte of rec cnt=0
- pop b ;retrieve bias adrs
- push b ;save it again
- call ghbcks ;get hex byte w/checksum
- mov e,a ;de now has record length
- ora a ;test it
- jnz notend ;jump if len<>0 (not eof rec)
- pop h ;all done
- ret
- notend: call ghbcks ;hi byte of rec ld adrs
- mov h,a ;accumulate in hl
- call ghbcks ;get lo byte
- mov l,a ;put lo in l
- lda lodflg ;test load flag
- ora a
- cz lodnit ;not first record, initialize
- push h ;save load address
- dad d ;add in record length
- dcx h ;make highest, not next
- lda hipc ;a new high?
- sub l
- lda hipc+1
- sbb h
- jnc notgt ;jump if not
- shld hipc ;yep, update hipc
- push d ;save reclen
- xchg ;load adrs to de
- lhld offset ;get offset to form true memory adrs
- dad d ;add in offset
- dad b ;and bias
- shld hiload ;mark highest true memory load adrs
- lda system+2 ;validate against top-mem pointer
- cmp h
- jc memful ;jump if out of memory
- pop d ;restore reclen
- notgt: pop h ;restore load address
- dad b ;add bias to load adrs
- push d ;save record length
- push h
- lhld bytcnt ;add record length to byte count
- dad d
- shld bytcnt
- pop h
- xchg
- lhld offset ;calculate true memory adrs
- dad d ;hl=true loading adrs
- pop d ;restore record length
- call ghbcks ;skip unused byte of intel format
- ;
- ; move the record into memory
- ;
- reclp: call ghbcks ;get hex byte
- mov m,a ;store it in buffer
- inx h ;point to next
- dcr e ;count down
- jnz reclp ;until record all read
- call ghbcks ;get checksum byte
- jnz cserr ;final add cksum should sum 0
- jmp loadlp ;good load, go do nxt record
- ;
- ; get next hex byte from input, and
- ; accumulate a checksum
- ;
- ghbcks: push b ;save em all
- push h
- push d
- call hexin ;get hex byte
- mov b,a ;save in b
- lxi h,cksum ;add to checksum
- mov a,m
- add b
- mov m,a
- mov a,b ;get byte back
- pop d ;restore checksum
- pop h ;restore other regs
- pop b
- ret
- ;
- ; routine to get next byte from input...forms
- ; byte from two ascii hex characters
- ;
- hexin: call gnb ;get next input file byte
- call hexval ;convert to binary w/validation
- rlc ;move into ms nybble
- rlc
- rlc
- rlc
- ani 0f0h ;kill possible garbage
- push psw ;save it
- call gnb ;get next byte
- call hexval ;convert it, w/validation
- pop b ;get back first
- ora b ;or in second
- ret ;good byte in a
- ;
- ; gnb - utility subroutine to get next
- ; byte from disk file
- gnb: push h ;save all regs
- push d
- push b
- lda bufptr ;get input bufr pointer
- ani 7fh ;wound back to 0?
- jz diskrd ;go read sector if so
- gnb1: mvi d,0 ;else form 16 bit offset
- mov e,a
- lxi h,tbuf ;from tbuf
- dad d ;add in offset
- mov a,m ;get next byte
- cpi eof ;end of file?
- jz eoferr ;error if so
- lxi h,bufptr ;else bump buf ptr
- inr m
- ora a ;return carry clear
- pop b ;restore and return
- pop d
- pop h
- ret
- ;
- ; read next sector from disk
- ;
- diskrd: mvi c,readf ;bdos "READ SEC" function
- lxi d,dfcb
- call bdos ;read sector
- ora a
- jnz eoferr ;error if phys end of file
- sta bufptr ;store 0 as new buf ptr
- jmp gnb1 ;go re-join gnb code
- ;
- ; load a com file
- ;
- lodcom: inr m ;bump the comfile flag
- lxi h,tpa ;set origin
- call lodnit ;and initialize
- xchg ;load address in de
- lhld bias ;add in bias
- dad d
- xchg
- lhld offset ;and offset
- dad d
- xchg ;de has absolute mem adrs of load
- ;
- comlp: lxi h,128 ;calculate next dma
- dad d
- lda system+2 ;check for space
- cmp h
- jc memful ;jump if none
- push h ;else save next dma
- push d ;and this dma
- mvi c,sdmaf ;set this dma
- call bdos
- lxi d,dfcb ;read next record
- mvi c,readf
- call bdos
- pop h ;recall this dma
- pop d ;de=next dma
- ora a ;end of read?
- jnz lodend ;jump if so
- lhld comsiz ;no, advance com byte count
- lxi b,128
- dad b
- shld comsiz
- jmp comlp ;continue
- ;
- lodend: dcx h ;one less byte is highest
- shld hiload ;set a new high
- lhld comsiz ;hi pc=bytecount+100h
- lxi d,tpa
- dad d
- xchg ;to de
- lhld bias ;add in bias
- dad d
- shld hipc
- lxi d,tbuf ;reset dma for hex files
- mvi c,sdmaf
- call bdos
- ret
- ;
- ; write output file
- ;
- wrtfil: lxi d,dfcb ;point to fcb
- push d ;save 2 copies of pointer
- push d
- call nitfcb ;initialize output fcb
- lxi h,outnam ;move output name in
- dcx d ;point to user # (prior to fcb)
- mvi b,10 ;move user, drive, primary name
- call move
- mov a,m ;output type blank?
- cpi ' '
- jnz wrtnb ;jump if not
- lxi h,outtyp ;yes, move dflt output filetype in
- wrtnb: mvi b,3
- call move
- pop d ;restore fcb pointer
- mvi c,erasef ;erase any existing file
- call bdos
- pop d ;restore fcb pointer
- mvi c,creatf ;create a new file
- call bdos
- inr a ;good create?
- jz dirful ;goto directory full error if not
- lhld hiload ;yep, get top of bufr pntr
- xchg ;in de
- lhld filbuf ;get start of bufr adrs
- mov a,e ;calculate output file size
- sub l
- mov c,a ;with result in bc
- mov a,d
- sbb h
- mov b,a
- mov a,b ;test length
- ora c
- jz loderr ;nothing to write???
- lxi d,dfcb ;get fcb pointer
- wrlp: push b ;save count
- push d ;and fcb pointer
- xchg ;get memory pointer to de
- lxi h,128 ;add in sector length for next pass
- dad d
- xthl ;save next dma
- push h ;above fcb
- mvi c,sdmaf ;set transfer address
- call bdos
- pop d ;fetch fcb pointer
- push d ;save it again
- mvi c,writef ;write a sector
- call bdos
- ora a ;test result
- jnz dskful ;disk full error...
- lhld reccnt ;no,increment count of records
- inx h
- shld reccnt
- pop d ;restore fcb pointer
- pop h ;and memory write pointer
- pop b ;and count
- mov a,c ;subtract 128 (sec size) from count
- sui 128
- mov c,a
- jnc wrlp ;jump if some left
- mov a,b ;hi-order borrow
- sui 1 ;do it (can't "DCR B", doesn't affect cy)
- mov b,a ;restore
- jnc wrlp ;jump if more left
- call closfl ;close output file
- ;
- ; report statistics to console
- ;
- call ilprnt
- db 'Loaded ',0
- lhld bytcnt ;print # bytes
- call decout
- call ilprnt
- db ' bytes (',0
- call hexout
- call ilprnt
- db 'H)',0
- call ilprnt
- db ' to file %',0
- lda comflg ;did we load a comfile too?
- ora a
- jz notcom ;jump if not
- call ilprnt
- db cr,lf,'Over a ',0
- lhld comsiz
- call decout
- call ilprnt
- db ' byte binary file',0
- notcom: call ilprnt
- db cr,lf,'Start address: ',0
- lhld lodadr ;print loading address
- call hexout
- call ilprnt
- db 'H Ending address: ',0
- lhld hipc ;print ending load address
- call hexout
- call ilprnt
- db 'H Bias: ',0
- lhld bias
- call hexout
- call ilprnt
- db 'H',cr,lf,0
- call ilprnt
- db 'Saved image size: ',0
- lhld reccnt ;get count of image records
- push h ;save it
- mvi b,7 ;convert to bytes
- xlp: dad h
- dcr b
- jnz xlp
- call decout ;print it
- call ilprnt
- db ' bytes (',0
- call hexout ;now in hex
- call ilprnt
- db 'H, - ',0
- pop h ;recall record count
- call decout ;print it
- call ilprnt
- db ' records)',cr,lf,0
- lhld lodadr ;fetch loading address
- mov a,l ;test if =tpa
- ora a
- jnz nottpa ;tpa always on page boundary
- mov a,h ;lo ok, test hi
- cpi (tpa shr 8) and 0ffh
- rz ;return if tpa
- nottpa: call ilprnt ;not, so print warning msg
- db cr,lf,bel
- db '++ Warning: program origin NOT at 100H ++'
- db cr,lf,0
- ret ;done
- ;
- ; ***********************
- ; * utility subroutines *
- ; ***********************
- ;
- ;
- ; routine to close any open file
- ;
- closfl: lxi d,dfcb
- mvi c,closef
- call bdos
- inr a ;test close result
- jz clserr ;jump if error
- ret
- ;
- ; print message in-line with code
- ;
- ilprnt: xthl ;message pntr to hl
- call prathl ;print it
- xthl ;restore and return
- ret
- ;
- ; print msg pointed to by hl until null. expand
- ; '%' char to current filename.
- ;
- prathl: mov a,m ;fetch char
- inx h ;point to next
- ora a ;terminator?
- rz ;then done
- cpi '%' ;want filename?
- jz prtfn ;go do it if so
- call type ;nope, just print char
- jmp prathl ;continue
- ;
- prtfn: push h ;save pointer
- push b
- lda dfcb ;fetch dr field of dfcb
- ora a ;default drive?
- jnz prndf ;jump if not
- call getdsk ;get logged-in drive #
- inr a ;make it one-relative (as in fcb)
- prndf: adi 'A'-1 ;make drive name printable
- call type ;print it
- lda dfcb-1 ;get user #
- cpi 0ffh ;null?
- cz getusr ;iff so, get current user
- mov l,a ;to hl
- mvi h,0
- call decout ;print it
- mvi a,':' ;drive names followed by colon
- call type
- lxi h,dfcb+1 ;setup for name
- mvi b,8 ;print up to 8
- call prtnam
- mvi a,'.' ;print dot
- call type
- mvi b,3 ;print filetype field
- call prtnam
- pop b
- pop h ;restore and continue
- jmp prathl
- ;
- ; print file name .HL max length in b. don't print spaces
- ;
- prtnam: mov a,m ;fetch a char
- cpi ' ' ;blank?
- jz pwind ;go wind if so
- inx h ;nope, move to next
- call type ;print it
- dcr b ;count down
- jnz prtnam ;continue
- ret
- pwind: inx h ;skip remainder of blank name
- dcr b
- jnz pwind
- ret
- ;
- ; print HL in decimal on console
- ;
- decout: push h ;save everybody
- push d
- push b
- lxi b,-10 ;conversion radix
- lxi d,-1
- declp: dad b
- inx d
- jc declp
- lxi b,10
- dad b
- xchg
- mov a,h
- ora l
- cnz decout ;this is recursive
- mov a,e
- adi '0'
- call type
- pop b
- pop d
- pop h
- ret
- ;
- ; newline on console
- ;
- crlf: mvi a,cr
- call type
- mvi a,lf
- jmp type
- ;
- ; print hl on console in hex
- ;
- hexout: mov a,h ;get hi
- call hexbyt ;print it
- mov a,l ;get lo, fall into hexbyt
- ;
- ; type accumulator on console in hex
- ;
- hexbyt: push psw ;save byte
- rar ;get ms nybble..
- rar ;..into lo 4 bits
- rar
- rar
- call nybble
- pop psw ;get back byte
- nybble: ani 0fh ;mask ms nybble
- adi 90h ;add offset
- daa ;decimal adjust a-reg
- aci 40h ;add offset
- daa ;fall into type
- ;
- ; type char in a on console
- ;
- type: push h ;save all
- push d
- push b
- mov e,a ;cp/m outputs from e
- mvi c,pcharf
- call bdos
- pop b
- pop d
- pop h
- ret
- ;
- ; move: from @hl to @de, count in b
- ;
- move: inr b ;up one
- movlp: dcr b ;count down
- rz ;return if done
- mov a,m ;not done, continue
- stax d
- inx h ;pointers=pointers+1
- inx d
- jmp movlp
- ;
- ; scan to first non-blank char in string @hl
- ;
- scanbk: inx h ;next
- mov a,m ;fetch it
- cpi ' '
- jz scanbk
- ret
- ;
- ; get hex digit and validate
- ;
- hexval: call hexdig ;get hex digit
- jc formerr ;jump if bad
- ret
- ;
- ; get hex digit, return cy=1 if bad digit
- ;
- hexdig: cpi '0' ;lo boundary test
- rc ;bad already?
- cpi '9'+1 ;no, test hi
- jc hexcvt ;jump if numeric
- cpi 'A' ;test alpha
- rc ;bad?
- cpi 'F'+1 ;no, upper alpha bound
- cmc ;pervert carry
- rc ;bad?
- sui 7 ;no, adjust to 0-f
- hexcvt: ani 0fh ;make it binary
- ret
- ;
- ; ******************
- ; * error handlers *
- ; ******************
- ;
- synerr: call crlf
- call ilprnt
- db ' Command line syntax error',cr,lf,cr,lf,0
- jmp help ;give help msg too
- ;
- afnerr: call errxit ;exit with message
- db 'Ambiguous file name: % not allowed.',0
- ;
- fnferr: call errxit
- db 'File % not found.',0
- ;
- dskful: call errxit
- db 'Disk full.',0
- ;
- dirful: call errxit
- db 'Directory full.',0
- ;
- eoferr: call errxit
- db 'Premature end-of-file in %',0
- ;
- cserr: call errxit
- db 'Checksum error in %',0
- ;
- clserr: call errxit
- db 'Can''t close %',0
- ;
- memful: call errxit
- db 'Memory full while loading %',0
- ;
- formerr:call errxit
- db 'Format error in file %',0
- ;
- loderr: call errxit
- db 'Writing %, nothing loaded',0
- ;
- help: call errxit ;print help text
- db 'MLOAD syntax:',cr,lf,cr,lf
- db 'MLOAD [<OUTFIL>=] <FILE1>[,<FILE2>...] [<BIAS>]',cr,lf
- db tab,' (brackets denote optional items)',cr,lf,cr,lf
- db tab,'<OUTFIL> is the optional output filename',cr,lf
- db tab,'<FILEn> are input file(s)',cr,lf
- db tab,'<BIAS> is a hex load offset within the output file'
- db cr,lf,cr,lf
- db tab,'<FILE1> may be an optional non-HEX file to be patched',cr,lf
- db tab,'by subsequently named HEX files (specifying',cr,lf
- db tab,'The filetype enables this function).'
- db cr,lf,cr,lf
- db 'Note that ZCPR2-style drive/user notation may be used in all'
- db cr,lf
- db 'file specifications (e.g., "B3:MYFILE.COM, "A14:MDM7.HEX").'
- db cr,lf,0
- ;
- ; general error handler
- ;
- errxit: call crlf ;new line
- pop h ;fetch error msg pointer
- call prathl ;print it
- call crlf
- jmp exit ;done
- ;
- ; initialize load parameters
- ;
- lodnit: mvi a,1 ;first record, set load flag
- sta lodflg
- shld lodadr ;save load address
- shld hipc ;and hi load
- push d ;save record length
- xchg ;de=load address
- lhld filbuf ;get address of file buffer
- mov a,l ;subtract load adrs from file buffer
- sub e
- mov l,a
- mov a,h
- sbb d
- mov h,a
- shld offset ;save as load offset
- push d ;save load address on stack
- push b ;save bias
- lxi d,outnam+2 ;check output filename
- ldax d ;(first char)
- cpi ' '
- jnz namskp ;jump if so
- lxi h,dfcb+1 ;get first name pointer
- mvi b,8 ;(don't include drive spec)
- call move
- ;
- ; check for outflg=1 (presence of an "="). note that the
- ; filename may well be blank, and yet outflg <>0, for example
- ; in the case of "A:=<FILENAME>" or "C4:=<FILENAME>". in
- ; this case, we want to remember the drive/user specified, but
- ; use the first input file to form the output name. otherwise,
- ; we use the current drive/user.
- ;
- lda outflg ;was there an "="?
- ora a
- jnz namskp ;jump if so
- lxi h,outnam ;get destination pointer
- call getusr ;get current user #
- mov m,a
- inx h ;point to drive
- call getdsk ;get it
- inr a ;fcb's drive is 1-relative
- mov m,a
- namskp: mvi a,1 ;insure "=" cannot occur anymore
- sta outflg
- pop b ;restore bias
- pop h ;load address to hl
- pop d ;restore record length
- ret
- ;
- ; *********************************
- ; * file name parsing subroutines *
- ; *********************************
- ;
- ; credit where credit's due:
- ; --------------------------
- ; these routines were lifted from bob van valzah's
- ; "FAST" program.
- ;
- ;
- ;
- ; *********************************
- ; * file name parsing subroutines *
- ; *********************************
- ;
- ;
- ; getfn gets a file name from text pointed to by reg hl into
- ; an fcb pointed to by reg de. leading delimeters are
- ; ignored. allows drive spec of the form <du:> (drive/user).
- ; this routine formats all 33 bytes of the fcb (but not ran rec).
- ;
- ; entry de first byte of fcb
- ; exit b=# of '?' in name
- ; fcb-1= user # parsed (if specified) or 255
- ;
- ;
- fparse: call nitfcb ;init 1st half of fcb
- call gstart ;scan to first character of name
- call getdrv ;get drive/user spec. if present
- mov a,b ;get user # or 255
- cpi 0ffh ;255?
- jz fpars1 ;jump if so
- dcx d ;back up to byte preceeding fcb
- dcx d
- stax d ;stuff user #
- inx d ;onward
- inx d
- fpars1: call getps ;get primary and secondary name
- ret
- ;
- ; nitfcb fills the fcb with dflt info - 0 in drive field
- ; all-blank in name field, and 0 in ex,s1,s2,rc, disk
- ; allocation map, and random record # fields
- ;
- nitfcb: push h
- push d
- call getusr ;init user field
- pop d
- pop h
- push d ;save fcb loc
- dcx d
- stax d ;init user # to currnt user #
- inx d
- xchg ;move it to hl
- mvi m,0 ;drive=default
- inx h ;bump to name field
- mvi b,11 ;zap all of name fld
- nitlp: mvi m,' '
- inx h
- dcr b
- jnz nitlp
- mvi b,33-11 ;zero others, up to nr field
- zlp: mvi m,0
- inx h
- dcr b
- jnz zlp
- xchg ;restore hl
- pop d ;restore fcb pointer
- ret
- ;
- ; gstart advances the text pointer (reg hl) to the first
- ; non delimiter character (i.e. ignores blanks). returns a
- ; flag if end of line (00h or ';') is found while scaning.
- ; exit hl pointing to first non delimiter
- ; a clobbered
- ; zero set if end of line was found
- ;
- gstart: call getch ;see if pointing to delim?
- rnz ;nope - return
- ora a ;physical end?
- rz ;yes - return w/flag
- inx h ;nope - move over it
- jmp gstart ;and try next char
- ;
- ; getdrv checks for the presence of a du: spec at the text
- ; pointer, and if present formats drive into fcb and returns
- ; user # in b.
- ;
- ; entry hl text pointer
- ; de pointer to first byte of fcb
- ; exit hl possibly updated text pointer
- ; de pointer to second (primary name) byte of fcb
- ; b user # if specified or 0ffh
- ;
- getdrv: mvi b,0ffh ;default no user #
- push h ;save text pointer
- dscan: call getch ;get next char
- inx h ;skip pointer over it
- jnz dscan ;scan until delimiter
- cpi ':' ;delimiter a colon?
- inx d ;skip dr field in fcb in case not
- pop h ;and restore text pointer
- rnz ;return if no du: spec
- mov a,m ;got one, get first char
- call cvtuc ;may be drive name, cvt to upper case
- cpi 'A' ;alpha?
- jc isnum ;jump to get user # if not
- sui 'A'-1 ;yes, convert from ascii to #
- dcx d ;back up fcb pointer to dr field
- stax d ;store drive # into fcb
- inx d ;pass pointer over drv
- inx h ;skip drive spec in text
- isnum: mov a,m ;fetch next
- inx h
- cpi ':' ;du delimiter?
- rz ;done then
- dcx h ;nope, back up text pointer
- mvi b,0 ;got a digit, init user value
- uloop: mov a,b ;get accumulated user #
- add a ;* 10 for new digit
- add a
- add b
- add a
- mov b,a ;back to b
- mov a,m ;get text char
- sui '0' ;make binary
- add b ;add to user #
- mov b,a ;updated user #
- inx h ;skip over it
- mov a,m ;get next
- cpi ':' ;end of spec?
- jnz uloop ;jump if not
- inx h ;yep, return txt pointer past du:
- ret
- ;
- ; getps gets the primary and secondary names into the fcb.
- ; entry hl text pointer
- ; exit hl character following secondary name (if present)
- ;
- getps: mvi c,8 ;max length of primary name
- mvi b,0 ;init count of '?'
- call getnam ;pack primary name into fcb
- mov a,m ;see if terminated by a period
- cpi '.'
- rnz ;nope - secondary name not given
- ;return default (blanks)
- inx h ;yup - move text pointer over period
- ftpoint:mov a,c ;yup - update fcb pointer to secondary
- ora a
- jz getft
- inx d
- dcr c
- jmp ftpoint
- getft: mvi c,3 ;max length of secondary name
- call getnam ;pack secondary name into fcb
- ret
- ;
- ; getnam copies a name from the text pointer into the fcb for
- ; a given maximum length or until a delimiter is found, which
- ; ever occurs first. if more than the maximum number of
- ; characters is present, character are ignored until a
- ; a delimiter is found.
- ; entry hl first character of name to be scanned
- ; de pointer into fcb name field
- ; c maximum length
- ; exit hl pointing to terminating delimiter
- ; de next empty byte in fcb name field
- ; c max length - number of characters transfered
- ;
- getnam: call getch ;are we pointing to a delimiter yet?
- rz ;if so, name is transfered
- inx h ;if not, move over character
- cpi '*' ;ambigious file reference?
- jz ambig ;if so, fill the rest of field with '?'
- cpi '?' ;afn reference?
- jnz notqm ;skip if not
- inr b ;else bump afn count
- notqm: call cvtuc ;if not, convert to upper case
- stax d ;and copy into name field
- inx d ;increment name field pointer
- dcr c ;if name field full?
- jnz getnam ;nope - keep filling
- jmp getdel ;yup - ignore until delimiter
- ambig: mvi a,'?' ;fill character for wild card match
- fillq: stax d ;fill until field is full
- inx d
- inr b ;increment count of '?'
- dcr c
- jnz fillq ;fall thru to ingore rest of name
- getdel: call getch ;pointing to a delimiter?
- rz ;yup - all done
- inx h ;nope - ignore antoher one
- jmp getdel
- ;
- ; getch gets the character pointed to by the text pointer
- ; and sets the zero flag if it is a delimiter.
- ; entry hl text pointer
- ; exit hl preserved
- ; a character at text pointer
- ; z set if a delimiter
- ;
- getch: mov a,m ;get the character, test for delim
- ;
- ; global entry: test char in a for filename delimiter
- ;
- fndelm: cpi '/'
- rz
- cpi '.'
- rz
- cpi ','
- rz
- cpi ' '
- rz
- cpi ':'
- rz
- cpi '='
- rz
- ora a ;set zero flag on end of text
- ret
- ;
- ; bdos entry: preserves bc, de. if system call is a file
- ; function, this routine logs into the drive/
- ; user area specified, then logs back after
- ; the call.
- ;
- bdos: call filfck ;check for a file function
- jnz bdos1 ;jump if not a file function
- call getdu ;get drive/user
- shld savedu
- ldax d ;get fcb's drive
- sta fcbdrv ;save it
- dcr a ;make 0-relative
- jm bdos0 ;if not default drive, jump
- mov h,a ;copy to h
- bdos0: xra a ;set fcb to default
- stax d
- dcx d ;get fcb's user #
- ldax d
- mov l,a
- inx d ;restore de
- call setdu ;set fcb's user
- ;
- ; note that unspecified user # (value=0ffh) becomes
- ; a getusr call, preventing ambiguity.
- ;
- call bdos1 ;do user's system call
- push psw ;save result
- push h
- lda fcbdrv ;restore fcb's drive
- stax d
- lhld savedu ;restore prior drive/user
- call setdu
- pop h ;restore bdos result registers
- pop psw
- ret
- ;
- ; local variables for bdos replacement routine
- ;
- savedu: dw 0 ;saved drive,user
- fcbdrv: db 0 ;fcb's drive
- dmadr: dw 80h ;current dma adrs
- ;
- bdos1: push d
- push b
- mov a,c ;doing setdma?
- cpi sdmaf
- jnz bdos1a ;jump if not
- xchg ;yep, keep a record of dma addresses
- shld dmadr
- xchg
- bdos1a: call system
- pop b
- pop d
- ret
- ;
- ; get drive, user: h=drv, l=user
- ;
- getdu: push b ;don't modify bc
- push d
- mvi c,gsuser ;get user #
- mvi e,0ffh
- call bdos1
- push psw ;save it
- mvi c,getdrf ;get drive
- call bdos1
- mov h,a ;drive returned in h
- pop psw
- mov l,a ;user in l
- pop d
- pop b ;restore caller's bc
- ret
- ;
- ; set drive, user: h=drv, l=user
- ;
- setdu: push b ;don't modify bc
- push d
- push h ;save info
- mov e,h ;drive to e
- mvi c,seldf ;set it
- call bdos1
- pop h ;recall info
- push h
- mov e,l ;user # to e
- mvi c,gsuser
- call bdos1 ;set it
- pop h
- pop d
- pop b
- ret
- ;
- ; check for file-function: open, close, read random, write
- ; random, read sequential, write sequential.
- ;
- filfck: mov a,c ;get function #
- cpi openf
- rz
- rc ;ignore lower function #'s
- cpi closef ;(they're not file-related)
- rz
- cpi readf
- rz
- cpi writef
- rz
- cpi rrand
- rz
- cpi wrand
- rz
- cpi fsrchf
- rz
- cpi fsrchn
- rz
- cpi erasef
- rz
- cpi creatf
- rz
- cpi filszf
- rz
- cpi srand
- ret
- ;
- ; convert char to upper case
- ;
- cvtuc: cpi 'a' ;check lo bound
- rc
- cpi 'z'+1 ;check hi
- rnc
- sui 20h ;convert
- ret
- ;
- ; check for hex filetype in fcb name
- ;
- hexchk: push h
- push d
- push b
- mvi b,3 ;type is 3 chars
- lxi d,dfcb+9 ;point de to type field
- lxi h,hextyp ;point hl to "COM"
- hexlop: ldax d
- ani 7fh ;ignore attributes
- cmp m
- inx h
- inx d
- jnz hexit ;jump if not com
- dcr b
- jnz hexlop
- hexit: pop b ;z reg has result
- pop d
- pop h
- ret
- ;
- hextyp: db 'HEX'
- ;
- ; routine to return user # without disturbing registers
- ;
- getusr: push h
- push d
- push b
- mvi c,gsuser
- mvi e,0ffh
- call bdos
- pop b
- pop d
- pop h
- ret
- ;
- ; routine to return drive # without disturbing registers
- ;
- getdsk: push h
- push d
- push b
- mvi c,getdrf
- call bdos
- pop b
- pop d
- pop h
- ret
- ;
- ; these are the initial values of the variables, and
- ; are moved into the variables area by the setup routine.
- ; if you add variables, be sure to add their intial value
- ; into this table in the order corresponding to their
- ; occurance in the variables section.
- ;
- varset: dw 0 ;bias
- dw 0 ;hiload
- dw 0 ;hipc
- db 0 ;cksum
- dw cmdbuf ;cmdptr
- db 0 ;bufptr
- db 0 ;lodflg
- dw cmdbuf ;filbuf
- dw 0 ;offset
- dw 0 ;lodadr
- db 0,0,' ' ;outnam
- dw 0 ;reccnt
- dw 0 ;bytcnt
- db 0 ;comflg
- dw 0 ;comsiz
- db 0 ;outflg
- ;
- varlen equ $-varset ;define length of init table
- ;
- ; working variables
- ;
- vars equ $ ;define variables area start
- ;
- bias: ds 2 ;load offset
- hiload: ds 2 ;highest true load address
- hipc: ds 2 ;highest pc
- cksum: ds 1 ;record checksum
- cmdptr: ds 2 ;command line pointer
- bufptr: ds 1 ;input buffer pointer
- lodflg: ds 1 ;something-loaded flag
- filbuf: ds 2 ;file buffer location
- offset: ds 2 ;load offset into buffer
- lodadr: ds 2 ;load address
- outnam: ds 13 ;output drive+name
- reccnt: ds 2 ;output file record count
- bytcnt: ds 2 ;output file bytes loaded count
- comflg: ds 1 ;flags com file encountered
- comsiz: ds 2 ;size of a loaded com file
- outflg: ds 1 ;flags an "=" present in cmd line
- ;
- ; end of working variables
- ;
- ;
- ;
- ; stack stuff
- ;
- spsave: ds 2 ;system stack pntr save
- ;
- ;
- ds 100 ;50-level stack
- ;
- stack equ $
- cmdbuf equ $ ;command buffer location
- ;
- ;
- end
-